package at.codebase.core.searchUser;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import at.codebase.core.StringTools;
import at.codebase.db.dao.EUserDao;
import at.codebase.db.entities.EUser;

import com.google.appengine.api.search.Document;
import com.google.appengine.api.search.Field;
import com.google.appengine.api.search.Field.Builder;
import com.google.appengine.api.search.GetRequest;
import com.google.appengine.api.search.GetResponse;
import com.google.appengine.api.search.Index;
import com.google.appengine.api.search.IndexSpec;
import com.google.appengine.api.search.MatchScorer;
import com.google.appengine.api.search.Query;
import com.google.appengine.api.search.QueryOptions;
import com.google.appengine.api.search.Results;
import com.google.appengine.api.search.ScoredDocument;
import com.google.appengine.api.search.SearchServiceFactory;
import com.google.appengine.api.search.SortOptions;

/**
 * 
 * @author Lukas Pfeifhofer <lukas.pfeifhofer@devlabs.pro>
 *
 */
public class UsernameSearch {
	
	public static void init(){}
	
	private static final Logger log = Logger.getLogger(UsernameSearch.class
			.getName());
	
	private static Index index = SearchServiceFactory.getSearchService().getIndex(IndexSpec.newBuilder().setName("UsernameIndex").build());
	private static final int SearchNumberAccuracy = 1000;
	
	static {
		UsernameSearch.rebuildIndex();
	}

	public static Index getIndex() {
		return index;
	}
	
	private static Document buildDocument(EUser user){
		Document.Builder builder = Document.newBuilder();
		builder.setId(user.getId().toString());

		Builder userNameSubBuilder = Field.newBuilder()
				.setName("usernameSubstrings")
				.setText(StringTools.generateSubstrings(user.getUsername()));
		Builder userNamePlainBuilder = Field.newBuilder()
				.setName("usernamePlain")
				.setText(user.getUsername());

		builder.addField(userNameSubBuilder);
		builder.addField(userNamePlainBuilder);
		
		return builder.build();
	}

	public static void putUser(EUser user) {
		getIndex().put(buildDocument(user));
	}
	
	public static void putUsers(final List<EUser> users) {
		ArrayList<Document> docs = new ArrayList<Document>();
		for(EUser user:users){
			docs.add(buildDocument(user));
		}
		getIndex().put(docs);
	}
	
	public static void dropUser(final EUser user) {
		getIndex().delete(new String[] { user.getId().toString() });
	}
	
	public static ArrayList<UsernameSearchResult> search(final String queryStr, final int limit, final int offset) {
		
		if(queryStr.matches("[a-zA-Z0-9äöüÄÖÜ]{2,}")){
			ArrayList<UsernameSearchResult> searchResults = new ArrayList<UsernameSearchResult>();
			
			
			Query queryObject = Query
					.newBuilder()
					.setOptions(
							QueryOptions
									.newBuilder()
									.setNumberFoundAccuracy(SearchNumberAccuracy)
									.setLimit(limit)
									.setOffset(offset)
									.setFieldsToSnippet("usernameSubstrings")
									.setSortOptions(
											SortOptions
													.newBuilder()
													.setMatchScorer(
															MatchScorer
																	.newBuilder()))
									.build()).build(queryStr);
			
			Results<ScoredDocument> queryResults = getIndex().search(queryObject);

			for (ScoredDocument doc : queryResults) {
				Iterable<Field> userNameFields = doc.getFields();
				String username = "";
				if (userNameFields != null) {
					for (Field field : userNameFields) {
						if ("usernamePlain".equals(field.getName())) {
							username = field.getText();
						}
					}
				}

				if(username != null && !username.trim().equals(""))
					searchResults.add(new UsernameSearchResult(new Long(doc.getId()), username));
			}
	
			return searchResults;
		}
		
		return new ArrayList<UsernameSearchResult>();
		
	}
	
	public static void showIndex(){
		GetRequest request = GetRequest.newBuilder()/*.setReturningIdsOnly(true)*/.build();
		GetResponse<Document> response = getIndex().getRange(request);
		if (response.getResults().isEmpty()) {
			return;
		}
		for (Document doc : response) {
			System.out.println(doc.getId());
			System.out.println(doc.toString());
		}
	}
	
	
	public static void rebuildIndex(){
		long startTime = System.currentTimeMillis();
		clearIndex();
		buildIndex();
		long endTime = System.currentTimeMillis();
		log.info("Searchindex (username) - rebuild - "+(endTime-startTime)+" ms");
	}
	
	public static void buildIndex(){
		long startTime = System.currentTimeMillis();
		List<EUser> users = EUserDao.getAllUsers();
		putUsers(users);
		long endTime = System.currentTimeMillis();
		log.info("Searchindex (username) - build - "+(endTime-startTime)+" ms");
	}
	
	public static void clearIndex() {
		long startTime = System.currentTimeMillis();
		while (true) {
			List<String> docIds = new ArrayList<String>();
			GetRequest request = GetRequest.newBuilder().setReturningIdsOnly(true).build();
			GetResponse<Document> response = getIndex().getRange(request);
			if (response.getResults().isEmpty()) {
				break;
			}
			for (Document doc : response) {
				docIds.add(doc.getId());
			}
			getIndex().delete(docIds);
		}
		long endTime = System.currentTimeMillis();
		log.info("Searchindex (username) - clear - "+(endTime-startTime)+" ms");
	}

}
